#include "Components/ComponentCreativeInventory.h"
#include "Components/ComponentPlayer.h"

#include "Engine/MathUtils.hpp"
#include "Managers/BlocksManager.h"
#include "Terrain/Terrain.h"
#include "Subsystems/BlockBehaviors/SubsystemBlockBehavior.hpp"
#include "Subsystems/BlockBehaviors/SubsystemBlockBehaviors.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Subsystems/SubsystemInventories.h"

#include "Managers/BlocksManager.h"
#include "Player/PlayerData.h"
#include "GameRegistry.hpp"

#include "Expand/PluginsManager.h"
#include "Expand/Plugins/AuthPlugin.h"

using namespace PocketSurvival::Engine;

namespace PocketSurvival
{
    void ComponentCreativeInventory::setActiveSlotIndex(uint32_t index)
    {
        activeSlotIndex = MathUtils::Clamp<uint32_t>(index, 0, getVisibleSlotsCount());
    }
    uint32_t ComponentCreativeInventory::getActiveSlotIndex()
    {
        return activeSlotIndex;
    }
    void ComponentCreativeInventory::setVisibleSlotsCount(uint32_t count)
    {
        count = MathUtils::Clamp<uint32_t>(count, 0, 10);
        if (count != visibleSlotsCount)
        {
            visibleSlotsCount = count;
            setActiveSlotIndex(activeSlotIndex);
        }
    }
    uint32_t ComponentCreativeInventory::getVisibleSlotsCount()
    {
        return 10;
    }
    uint32_t ComponentCreativeInventory::slotsCount()
    {
        return slotValues.size();
    }
    void ComponentCreativeInventory::setSlotValue(uint32_t slotIndex, int32_t cellValue, uint32_t valueCount)
    {
        if (slotIndex < slotValues.size())
        {
            slotValues[slotIndex] = cellValue;
        }
    }
    int32_t ComponentCreativeInventory::getSlotValue(uint32_t slotIndex)
    {
        if (slotIndex < slotValues.size())
        {
            return slotValues[slotIndex];
        }
        return 0;
    }
    uint32_t ComponentCreativeInventory::getSlotCount(uint32_t slotIndex)
    {
        if (slotIndex < slotValues.size() && slotValues[slotIndex] != 0)
        {
            return 9999;
        }
        return 0;
    }
    void ComponentCreativeInventory::onSlotChange(uint32_t slotIndex)
    {
        GameSingleton::singletonPtr->getSingleton<SubsystemInventories>()->pushSyncItem(this, slotIndex);
    }
    uint32_t ComponentCreativeInventory::getSlotCapacity(uint32_t slotIndex, int32_t cellValue)
    {
        if (slotIndex < openSlotsCount)
        {
            return 99980001;
        }
        if (GameSingleton::blocksManager->blocks[Terrain::ExtractContents(cellValue)]->IsNonDuplicable)
        {
            return 9999;
        }
        return 99980001;
    }
    uint32_t ComponentCreativeInventory::getSlotProcessCapacity(uint32_t slotIndex, int32_t cellValue)
    {
        int32_t slotCount = getSlotCount(slotIndex);
        int32_t slotValue = getSlotValue(slotIndex);
        if (slotCount > 0 && slotValue != 0)
        {
            std::vector<SubsystemBlockBehavior *> &blockBehaviors = GameSingleton::singletonPtr->getSingleton<SubsystemBlockBehaviors>()->getBlockBehaviors(Terrain::ExtractContents(slotValue));
            for (auto behaviorIt = blockBehaviors.begin(); behaviorIt != blockBehaviors.end(); ++behaviorIt)
            {
                int32_t processInventoryItemCapacity = (*behaviorIt)->getProcessInventoryItemCapacity(this, slotIndex, cellValue);
                if (processInventoryItemCapacity > 0)
                {
                    return processInventoryItemCapacity;
                }
            }
        }
        if (slotIndex < openSlotsCount)
        {
            return 0;
        }
        return 9999;
    }
    void ComponentCreativeInventory::addSlotItems(uint32_t slotIndex, int32_t cellValue, uint32_t count)
    {
        addNetSlotItems(slotIndex, cellValue, count);
        onSlotChange(slotIndex);
    }
    bool ComponentCreativeInventory::addNetSlotItems(uint32_t slotIndex, int32_t cellValue, uint32_t count)
    {
        if (slotIndex < openSlotsCount)
        {
            if (slotValues[slotIndex] != cellValue)
            {
                slotValues[slotIndex] = cellValue;
                return true;
            }
        }
        return false;
    }
    void ComponentCreativeInventory::processSlotItems(IInventory *sourceInventory, uint32_t sourceSlotIndex, uint32_t slotIndex, int32_t cellValue, uint32_t count, uint32_t processCount, int32_t &outProcessedValue, uint32_t &outProcessedCount)
    {
        int slotCount = getSlotCount(slotIndex);
        int slotValue = getSlotValue(slotIndex);
        if (slotCount > 0 && slotValue != 0)
        {
            std::vector<SubsystemBlockBehavior *> &blockBehaviors = GameSingleton::singletonPtr->getSingleton<SubsystemBlockBehaviors>()->getBlockBehaviors(Terrain::ExtractContents(slotValue));
            for (auto behaviorIt = blockBehaviors.begin(); behaviorIt != blockBehaviors.end(); ++behaviorIt)
            {
                int32_t processInventoryItemCapacity = (*behaviorIt)->getProcessInventoryItemCapacity(this, slotIndex, cellValue);
                if (processInventoryItemCapacity > 0)
                {
                    (*behaviorIt)->processInventoryItem(this, slotIndex, cellValue, count, MathUtils::Min<int32_t>(processInventoryItemCapacity, processCount), outProcessedValue, outProcessedCount);
                    return;
                }
            }
        }
        if (slotIndex >= openSlotsCount)
        {
            outProcessedValue = 0;
            outProcessedCount = 0;
        }
        else
        {
            outProcessedValue = cellValue;
            outProcessedCount = count;
        }
    }
    uint32_t ComponentCreativeInventory::removeSlotItems(uint32_t slotIndex, uint32_t count)
    {
        count = removeNetSlotItems(slotIndex, count);
        onSlotChange(slotIndex);
        return count;
    }
    uint32_t ComponentCreativeInventory::removeNetSlotItems(uint32_t slotIndex, uint32_t count)
    {
        if (slotIndex < openSlotsCount)
        {
            int num = Terrain::ExtractContents(slotValues[slotIndex]);
            if (GameSingleton::blocksManager->blocks[num]->IsNonDuplicable)
            {
                slotValues[slotIndex] = 0;
            }
            if (count >= 9999)
            {
                slotValues[slotIndex] = 0;
            }
        }
        return 1;
    }
    void ComponentCreativeInventory::dropAllItems(const Vector3 &position)
    {
    }
    void ComponentCreativeInventory::dropSlotItems(uint32_t slotIndex, const Vector3 &position, const Vector3 &velocity)
    {
    }

    static const std::string componentName = "CreativeInventory";
    const std::string &ComponentCreativeInventory::getName() const
    {
        return componentName;
    }
    const std::string &ComponentCreativeInventory::GetName()
    {
        return componentName;
    }

    static std::mutex creativeInventoryMutex;
    static const std::vector<int32_t> &getCreativeDisplaySlots()
    {
        std::lock_guard<std::mutex> lk(creativeInventoryMutex);
        static std::vector<int32_t> creativeDisplaySlotValue;

        if (creativeDisplaySlotValue.size() == 0)
        {
            std::vector<Block *> displayBlockVec;
            std::vector<Block *> &blocks = GameSingleton::blocksManager->blocks;
            for (int32_t content = 0; content < blocks.size(); ++content)
            {
                if (blocks[content] != nullptr)
                {
                    displayBlockVec.push_back(blocks[content]);
                }
            }
            std::sort(displayBlockVec.begin(), displayBlockVec.end(), [](Block *blockValue1, Block *blockValue2) -> bool
                      { return blockValue1->DisplayOrder < blockValue2->DisplayOrder; });

            // spdlog::warn("start");
            std::vector<int32_t> creativeValues;
            for (int32_t index = 0; index < displayBlockVec.size(); ++index)
            {
                creativeValues.clear();
                displayBlockVec[index]->getCreativeValues(creativeValues);
                for (int32_t index2 = 0; index2 < creativeValues.size(); ++index2)
                {
                    // spdlog::info("{}",creativeValues[index2]);
                    creativeDisplaySlotValue.push_back(creativeValues[index2]);
                }
            }
            // spdlog::warn("end");
        }
        return creativeDisplaySlotValue;
    }

    void ComponentCreativeInventory::load(const nlohmann::json &jsonData)
    {
        componentPlayer = GameSingleton::gameRegistry->get<ComponentPlayer *>(entity);
        PlayerData *playerData = GameSingleton::gameRegistry->get<PlayerData *>(entity);

        openSlotsCount = jsonData["OpenSlotsCount"].get<uint32_t>();
        activeSlotIndex = jsonData["ActiveSlotIndex"].get<uint32_t>();
        categoryIndex = jsonData["CategoryIndex"].get<uint32_t>();
        pageIndex = jsonData["PageIndex"].get<uint32_t>();

        slotValues = std::vector<int32_t>(openSlotsCount, 0);
        if (playerData->gameMode == GameMode::Creative)
        {
            const std::vector<int32_t> &displayBlocks = getCreativeDisplaySlots();
            for (int32_t blockValue : displayBlocks)
            {
                slotValues.push_back(blockValue);
            }

            if (slotValues.size() > 0)
            {
                // 待完成 ... 
                // uint32_t authLevel = GameSingleton::singletonPtr->getSingleton<PluginsManager>()->findPlugin<AuthPlugin>()->getLevelByGuid(playerData->playerGuid);
                // if (authLevel >= 100)
                // {
                //     slotValues[openSlotsCount] = 1;
                // }
            }

            if (jsonData.find("Slots") != jsonData.end())
            {
                uint32_t slotCount = jsonData["Slots"].size();
                slotCount = slotCount > openSlotsCount ? openSlotsCount : slotCount;
                for (int32_t index = 0; index < slotCount; ++index)
                {
                    slotValues[index] = jsonData["Slots"][index].get<int32_t>();
                }
            }
        }
    }
    void ComponentCreativeInventory::save(nlohmann::json &jsonData)
    {
        jsonData["ActiveSlotIndex"] = activeSlotIndex;
        jsonData["CategoryIndex"] = categoryIndex;
        jsonData["PageIndex"] = pageIndex;
        jsonData["Slots"] = nlohmann::json::array();
        for (int32_t index = 0; index < openSlotsCount; ++index)
        {
            jsonData["Slots"].push_back(slotValues[index]);
        }
    }
    void ComponentCreativeInventory::saveToMsgJson(nlohmann::json &jsonData)
    {
        MSG_VALUE_SET(jsonData, "ActiveSlotIndex", "int", activeSlotIndex);
        MSG_VALUE_SET(jsonData, "CategoryIndex", "int", categoryIndex);
        MSG_VALUE_SET(jsonData, "PageIndex", "int", pageIndex);
        MSG_VALUE_SET(jsonData, "Id", "int", id);
        jsonData["Slots"] = nlohmann::json::object();
        for (int32_t index = 0; index < openSlotsCount; ++index)
        {
            if (slotValues[index] == 0)
            {
                continue;
            }
            nlohmann::json slotsJson = nlohmann::json::object();
            MSG_VALUE_SET(slotsJson, "Contents", "int", slotValues[index]);
            jsonData["Slots"][fmt::format("Slot{}", index)] = slotsJson;
        }
    }

} // namespace PocketSurvival
